package snapex.core.security;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import lombok.extern.slf4j.Slf4j;
import snapex.core.security.JwtTokenStoreConfiguration.JwtTokenStoreCondition;

@Slf4j
@Configuration
@Conditional(JwtTokenStoreCondition.class)
public class JwtTokenStoreConfiguration {
	
	@Value("${snapex.security.keyStorePassword}")
	String keyStorePassword;
	@Value("${snapex.security.keyStorePath}")
	String keyStorePath;	
	@Value("${snapex.security.keyStoreAlias}")
	String keyStoreAlias;	
	
	@Bean("JwtTokenStore")
	@Primary
	public TokenStore tokenStore() {
		return new JwtTokenStore(accessTokenConverter());
	}
	
	@Bean("JwtTokenEnhancer")
	@Primary
	public TokenEnhancer tokenEnhancer() {
	    return accessTokenConverter();
	}
	
//	@Autowired	
//	private PasswordEncoder passwordEncoder;
//	
//    @Bean   
//    public PasswordEncoder passwordEncoder() {
//        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
//    }
    
	@Bean("JwtAccessTokenConverter")
	public JwtAccessTokenConverter accessTokenConverter() {
		
		JwtAccessTokenConverter converter = new JwtAccessTokenConverter() {
			@Override
			public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
				
				log.debug("*********JwtAccessTokenConverter:enhance accessToken:{}",accessToken);
				
				Map<String, Object> customInfo = new HashMap();

				//customInfo.put("organization", "snapex");
				
				//customInfo.put("principal", authentication.getPrincipal());
				
				((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(customInfo);
							
				return super.enhance(accessToken, authentication);
			}			
		};
		log.debug("*********JwtAccessTokenConverter keyStorePath:{}, keyStoreAlias:{}",keyStorePath, keyStoreAlias);
		Resource keyStoreResource = null;
		if(keyStorePath.startsWith("classpath:")) {
			keyStoreResource = new ClassPathResource(keyStorePath.replaceFirst("classpath:",""));
		}else {
			keyStoreResource = new FileSystemResource(keyStorePath);
		}
				
//		if(keyStorePassword.startsWith("{bcrypt}")) {
//			keyStorePassword = keyStorePassword.replaceFirst("{noop}", "");
//		}else {
//			
//		}
		
		KeyStoreKeyFactory factory = new KeyStoreKeyFactory(keyStoreResource,keyStorePassword.toCharArray());
		
		converter.setKeyPair(factory.getKeyPair(keyStoreAlias));
		
		return converter;
	}
	


	static class JwtTokenStoreCondition extends SpringBootCondition{

		@Override
		public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
	
			ConditionMessage.Builder message = ConditionMessage.forCondition("snapex.security.tokenStore Condition");
			
			String tokenStore = context.getEnvironment().getProperty("snapex.security.tokenStore");
			if("JWT".equals(tokenStore)) {				
				return ConditionOutcome.match(message.foundExactly("JWT tokenStore"));
			}
			return ConditionOutcome.noMatch(message.didNotFind("JWT tokenStore").atAll());
		}	
	}
}

